package com.example.lotterysystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.lotterysystem.common.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.common.enums.ActivityStatusEnum;
import com.example.lotterysystem.common.enums.GlobalErrorCodeEnum;
import com.example.lotterysystem.common.enums.PrizeGradeEnum;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.LotteryParam;
import com.example.lotterysystem.controller.param.WinningRecordListParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.mapper.*;
import com.example.lotterysystem.service.LotteryService;
import com.example.lotterysystem.service.dto.WinningRecordDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;
@Slf4j
@Service
public class LotteryServiceImpl implements LotteryService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;
    
    @Autowired
    private RedisUtil redisUtil;

    private final String PREFIX = "winning_record";
    private final String ACTIVITY_ID = "_activity_id";
    private final String PRIZE_ID = "_prize_id";
    private final Long WINNING_RECORD_TTL = 60 * 60 * 24 * 3L;
    @Override
    public void lotteryPrize(LotteryParam param) {
        // 发送抽奖消息

        Map<String, String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        log.info("mq 消息发送成功, map={}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public boolean checkLotteryParam(LotteryParam param) {
        if(param == null){
            log.warn(GlobalErrorCodeEnum.LOTTERY_PRIZE_ERROR.getMsg() +
                    ": 抽奖请求参数 param 为空");
            return false;
        }

        // 1. 校验抽奖请求是否有效
        Activity activity = activityMapper.selectById(param.getActivityId());
        ActivityPrize activityPrize = activityPrizeMapper.selectOne(
                new QueryWrapper<ActivityPrize>().eq("activity_id", param.getActivityId())
                        .eq("prize_id", param.getPrizeId()));
        if(activity == null || activityPrize == null){
            log.warn(GlobalErrorCodeEnum.LOTTERY_PRIZE_ERROR.getMsg() +
                    ": 抽奖活动或关联的奖品不存在");
            return false;
        }

        // 活动是否有效
        if(activity.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            log.warn(GlobalErrorCodeEnum.LOTTERY_PRIZE_ERROR.getMsg() +
                    ": 抽奖活动已完成");
            return false;
        }

        // 奖品是否有效
        if(activityPrize.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            log.warn(GlobalErrorCodeEnum.LOTTERY_PRIZE_ERROR.getMsg() +
                    ": 奖品已被抽取");
            return false;
        }

        // 中奖者人数和设置的奖品数量是否一致
        if(param.getWinnerList().size() != activityPrize.getPrizeAmount()){
            log.warn(GlobalErrorCodeEnum.LOTTERY_PRIZE_ERROR.getMsg() +
                    ": 中奖人数与奖品数量不一致");
            return false;
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<WinningRecord> saveLuckyRecords(LotteryParam lotteryParam) {
        //查询 活动,奖品批次的获奖人员,奖品,活动奖品关联表(奖品数量,奖品等级),获奖时间并保存
        Activity activity = activityMapper.selectById(lotteryParam.getActivityId());
        List<Long> userIds = lotteryParam.getWinnerList().stream()
                .map(LotteryParam.Lucky::getUserId).toList();
        List<User> userList = userMapper.selectList(
                new QueryWrapper<User>().in("id", userIds));
        Prize prize = prizeMapper.selectById(lotteryParam.getPrizeId());
        ActivityPrize activityPrize = activityPrizeMapper.selectOne(
                new QueryWrapper<ActivityPrize>()
                        .eq("activity_id", lotteryParam.getActivityId())
                        .eq("prize_id", lotteryParam.getPrizeId()));
        // 构造中奖记录并入库
        List<WinningRecord> winningRecords = userList.stream()
                .map( user -> {
                    WinningRecord winningRecord = new WinningRecord();
                    winningRecord.setActivityId(activity.getId());
                    winningRecord.setActivityName(activity.getActivityName());
                    winningRecord.setPrizeId(prize.getId());
                    winningRecord.setPrizeName(prize.getName());
                    winningRecord.setPrizeTier(activityPrize.getPrizeTiers());
                    winningRecord.setWinnerId(user.getId());
                    winningRecord.setWinnerName(user.getUserName());
                    winningRecord.setWinnerEmail(user.getEmail());
                    winningRecord.setWinnerPhoneNumber(user.getPhoneNumber().getValue());
                    winningRecord.setWinningTime(lotteryParam.getWinningTime());
                    return winningRecord;
                }).toList();
        winningRecordMapper.batchInsert(winningRecords);

        // 缓存   1. 已奖品为维度(key: ?_activityId_prizeId, value: winningRecords)
        cacheWiningRecords(PREFIX + ACTIVITY_ID + activity.getId() + PRIZE_ID + prize.getId(),
                winningRecords, WINNING_RECORD_TTL);
        //       2. 已活动为维度(key: ?_activityId, value: winningRecords) (活动须已完成)
        if(activity.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {

            List<WinningRecord> activityWinningRecord = winningRecordMapper.selectList(
                    new QueryWrapper<WinningRecord>()
                            .eq("activity_id", activity.getId()));
            cacheWiningRecords(PREFIX + ACTIVITY_ID + activity.getId(),
                    activityWinningRecord, WINNING_RECORD_TTL);
        }
        return winningRecords;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(activityId == null){
            log.warn("要删除的活动记录id 为空");
        }
        QueryWrapper<WinningRecord> wrapper =
                new QueryWrapper<WinningRecord>()
                        .eq("activity_id", activityId);
        if(prizeId != null){
            wrapper = wrapper.eq("prize_id", prizeId);
        }
        // 删除活动表
        winningRecordMapper.delete(wrapper);
        // 删除缓存
        if (prizeId != null) {
            deleteWinningRecords(PREFIX + ACTIVITY_ID + activityId + PRIZE_ID + prizeId);
        }
        deleteWinningRecords(PREFIX + ACTIVITY_ID + activityId);
    }

    @Override
    public List<WinningRecordDTO> getWinningRecordList(WinningRecordListParam param) {
        if(param.getActivityId() == null){
            return null;
        }
        String key = param.getPrizeId() == null
                ? PREFIX + ACTIVITY_ID + param.getActivityId()
                : PREFIX + ACTIVITY_ID + param.getActivityId() + PRIZE_ID + param.getPrizeId();
        List<WinningRecord> cacheWiningRecord = getCacheWiningRecord(key);
        // 查缓存，存在则直接返回
        if(cacheWiningRecord != null && !CollectionUtils.isEmpty(cacheWiningRecord)){
            return transfer(cacheWiningRecord);
        }
        // 不存在，查表，写入缓存，再返回
        QueryWrapper<WinningRecord> wrapper = new QueryWrapper<WinningRecord>()
                        .eq("activity_id", param.getActivityId());
        if(param.getPrizeId() != null){
            wrapper = wrapper.eq("prize_id", param.getPrizeId());
        }
        List<WinningRecord> winningRecords = winningRecordMapper.selectList(wrapper);
        if(winningRecords == null){
            log.info("要查找的中奖记录在数据库中不存在");
            return null;
        }
        cacheWiningRecords(key, winningRecords, WINNING_RECORD_TTL);
        return transfer(winningRecords);
    }

    private List<WinningRecordDTO> transfer(List<WinningRecord> winningRecords){
        return winningRecords.stream()
                .map(winningRecord -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winningRecord.getWinnerId());
                    winningRecordDTO.setWinnerName(winningRecord.getWinnerName());
                    winningRecordDTO.setPrizeName(winningRecord.getPrizeName());
                    winningRecordDTO.setPrizeTier(PrizeGradeEnum.forName(winningRecord.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winningRecord.getWinningTime());
                    return winningRecordDTO;
                }).toList();
    }
    private void cacheWiningRecords(String key, List<WinningRecord> winningRecords, Long ttl) {
        String value = "";
        try {
            value = JacksonUtil.writeValueAsString(winningRecords);
            redisUtil.set(key, value, ttl);
        } catch (Exception e){
            log.error("缓存中奖记录异常, key: {}, value: {}", key, value, e);
        }
    }

    private void deleteWinningRecords(String key){
        try {
            if(redisUtil.hasKey(key)){
                redisUtil.del(key);
            }
        } catch (Exception e){
            log.warn("删除缓存记录异常, key: {}", key, e);
        }
    }
    private List<WinningRecord> getCacheWiningRecord(String key){
        try {
            if (!StringUtils.hasText(key)) {
                log.error("获取缓存失败, key 为空");
                return null;
            }
            return JacksonUtil.readListValue(redisUtil.get(key), WinningRecord.class);
        } catch (Exception e){
            log.error("获取缓存失败 key: {}", key, e);
            return null;
        }
    }
}
